#include "board.h"
#include <rtdevice.h>
#include <rtdef.h>

typedef struct
{
    struct rt_spi_bus bus;
    SPI_TypeDef *spi;
} dspi_t;

static void spi2_init(void)
{
    SPI_InitTypeDef SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;

    /*!< SPI2 SCK pin configuration */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /*!< SPI2 MOSI pin configuration */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /*!< SPI2 MISO pin configuration */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                      //主模式
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  //数据大小8位
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                         //时钟极性，空闲时为低
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                       //第1个边沿有效，上升沿为采样时刻
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                          //NSS信号由软件产生
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //4分频
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                 //高位在前
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &SPI_InitStructure);
    /* Enable SPI2 */
    SPI_Cmd(SPI2, ENABLE);
}

static rt_err_t spi_configure(struct rt_spi_device *device,
                              struct rt_spi_configuration *configuration)
{
    return 0;
}

static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    dspi_t *ds = (dspi_t *)device->bus;

    if (message->cs_take)
    {
        rt_pin_write(device->cs_pin, device->cs_active);
    }

    if (message->length > 0)
    {
        uint8_t b;
        int len = message->length;
        uint8_t *recv = message->recv_buf;
        uint8_t *send = (uint8_t *)message->send_buf;

        while (len-- > 0)
        {
            b = send ? *(send++) : 0xFF;

            while (SPI_I2S_GetFlagStatus(ds->spi, SPI_I2S_FLAG_TXE) == RESET)
                ;

            SPI_I2S_SendData(ds->spi, b);
            while (SPI_I2S_GetFlagStatus(ds->spi, SPI_I2S_FLAG_RXNE) == RESET)
                ;

            b = SPI_I2S_ReceiveData(ds->spi);
            if (recv)
                *(recv++) = b;
        }
    }

    if (message->cs_release)
    {
        rt_pin_write(device->cs_pin, !device->cs_active);
    }

    return message->length;
}

static const struct rt_spi_ops _spi_ops = {
    .configure = spi_configure,
    .xfer = spixfer,
};

static dspi_t _spi2 = {
    .spi = SPI2,
};

static int attach_cs_8(void)
{
    static struct rt_spi_device _spi20;
    struct rt_spi_device *spi_device = &_spi20;
    int cspin;
    int active_level = 0;

    cspin = rt_pin_get("pb.8");
    spi_device->cs_pin = cspin;
    spi_device->cs_active = active_level;

    rt_pin_mode(cspin, PIN_MODE_OUTPUT);
    rt_pin_write(cspin, !active_level);

    return rt_spi_bus_attach_device(spi_device, "acc", "spi2", 0);
}

static int attach_cs_9(void)
{
    static struct rt_spi_device _spi21;
    struct rt_spi_device *spi_device = &_spi21;
    int cspin;
    int active_level = 0;

    cspin = rt_pin_get("pb.9");
    spi_device->cs_pin = cspin;
    spi_device->cs_active = active_level;

    rt_pin_mode(cspin, PIN_MODE_OUTPUT);
    rt_pin_write(cspin, !active_level);

    return rt_spi_bus_attach_device(spi_device, "gry", "spi2", 0);
}

int spi_drv_init(void)
{
    spi2_init();

    rt_spi_bus_register(&_spi2.bus, "spi2", &_spi_ops);

    attach_cs_8();
    attach_cs_9();

    return 0;
}
INIT_PREV_EXPORT(spi_drv_init);
